﻿using System.Collections.Immutable;
using System.Linq;
using System.Reflection;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using UIOC.CodeAnalysis.Diagnostics;
using UIOC.CodeAnalysis.Extensions;
using UIOC.CodeAnalysis.Helpers;

namespace UIOC.CodeAnalysis.Analyzers;

[DiagnosticAnalyzer(LanguageNames.CSharp)]
internal sealed class TypeMapArgumentAnalyzer : DiagnosticAnalyzer {

    private static void LogInfo(string message) {
        SourceOutputHelper.LogInfo(DiagnosticSeverity.Info, $"[{nameof(TypeMapArgumentAnalyzer)}] {message}");
    }

    public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics =>
        ImmutableArray.Create(TypeMapArgumentMustBeInterfaceDiagnostic.Rule, TypeMapArgumentMissingImplementationDiagnostic.Rule);

    public override void Initialize(AnalysisContext context) {
        context.EnableConcurrentExecution();
        context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
        context.RegisterSyntaxNodeAction(AnalyzeInvocationExpression, SyntaxKind.InvocationExpression);
    }

    private void AnalyzeInvocationExpression(SyntaxNodeAnalysisContext context) {
        var compilation = context.Compilation;
        var assembly = compilation.Assembly;
        if (assembly.IsIgnore()) {
            return;
        }

        if (context.Node is not InvocationExpressionSyntax invocationExpressionSyntax) {
            return;
        }

        if (invocationExpressionSyntax.Expression is not MemberAccessExpressionSyntax memberAccessExpressionSyntax) {
            return;
        }

        var expressionText = memberAccessExpressionSyntax.Expression.ToString();
        if (expressionText != TypeFullNames.TypeMap) {
            return;
        }

        var implementAttributeSymbol = compilation.GetTypeByMetadataName(AttributeFullNames.Implement);
        if (implementAttributeSymbol == null) {
            return;
        }

        AutowiredAttributeImplementationAnalyzer.CollectAllImplementTypes(context, implementAttributeSymbol);

        INamedTypeSymbol argumentTypeSymbol, argumentTypeSymbol1, argumentTypeSymbol2 = null;
        var isGeneric = false;
        (var requireReport, argumentTypeSymbol1) = CheckGeneric(memberAccessExpressionSyntax, context);
        if (!requireReport) {
            (requireReport, argumentTypeSymbol2) = CheckIdentifier(memberAccessExpressionSyntax, invocationExpressionSyntax, context);
        } else {
            isGeneric = true;
        }
        argumentTypeSymbol = argumentTypeSymbol1 ?? argumentTypeSymbol2;
        if (!requireReport) {
            if (argumentTypeSymbol != null) {
                if (!AutowiredAttributeImplementationAnalyzer.ContainsImplementInterface(argumentTypeSymbol)) {
                    context.ReportDiagnostic(Diagnostic.Create(
                        TypeMapArgumentMissingImplementationDiagnostic.Rule,
                        invocationExpressionSyntax.GetLocation(),
                        argumentTypeSymbol.GetShortName(),
                        invocationExpressionSyntax.ToString(),
                        isGeneric ? "泛型参数 'TInterface' " : "参数 'interfaceType' "));
                }
            }
            return;
        }

        context.ReportDiagnostic(Diagnostic.Create(
            TypeMapArgumentMustBeInterfaceDiagnostic.Rule,
            invocationExpressionSyntax.GetLocation(),
            argumentTypeSymbol.GetShortName(),
            invocationExpressionSyntax.ToString(),
            isGeneric ? "泛型参数 'TInterface' " : "参数 'interfaceType' "));
    }

    private (bool requireReport, INamedTypeSymbol typeSymbol) CheckGeneric(MemberAccessExpressionSyntax memberAccessExpressionSyntax, SyntaxNodeAnalysisContext context) {

        if (memberAccessExpressionSyntax.Name is not GenericNameSyntax genericNameSyntax || (genericNameSyntax.Identifier.Text != MemberNames.TypeMap.Instantiate && genericNameSyntax.Identifier.Text != MemberNames.TypeMap.Fetch)) {
            return (false, null);
        }

        if (genericNameSyntax.TypeArgumentList.Arguments.Count == 0)
            return (false, null);

        var typeArgumentSyntax = genericNameSyntax.TypeArgumentList.Arguments[0];
        var typeArgumentSymbol = context.SemanticModel.GetTypeInfo(typeArgumentSyntax).Type;
        if (typeArgumentSymbol is not INamedTypeSymbol instantiateTypeSymbol) {
            return (false, null);
        }

        if (instantiateTypeSymbol.TypeKind == TypeKind.Interface) {
            return (false, instantiateTypeSymbol);
        }

        return (true, instantiateTypeSymbol);
    }

    private (bool requireReport, INamedTypeSymbol typeSymbol) CheckIdentifier(MemberAccessExpressionSyntax memberAccessExpressionSyntax, InvocationExpressionSyntax invocationExpressionSyntax, SyntaxNodeAnalysisContext context) {

        if (memberAccessExpressionSyntax.Name is not IdentifierNameSyntax identifierNameSyntax || (identifierNameSyntax.Identifier.Text != MemberNames.TypeMap.Instantiate && identifierNameSyntax.Identifier.Text != MemberNames.TypeMap.Fetch)) {
            return (false, null);
        }
        var arguments = invocationExpressionSyntax.ArgumentList?.Arguments;
        if (arguments?.Count > 0) {
            // 查找目标参数：优先匹配名称为 interfaceType 的参数，否则取第一个参数
            var targetArgument = FindTargetArgument(invocationExpressionSyntax.ArgumentList) ?? throw new System.InvalidProgramException();
            var (isInterface, typeSymbol) = IsInterfaceTypeExpression(targetArgument.Expression, context.SemanticModel);
            if (isInterface) {
                return (false, typeSymbol);
            }
            return (true, typeSymbol);
        }
        return (false, null);
    }

    /// <summary>
    /// 查找目标参数：优先按名称（interfaceType），否则取第一个参数
    /// </summary>
    private ArgumentSyntax FindTargetArgument(ArgumentListSyntax argumentList) {
        var namedArgument = argumentList.Arguments.FirstOrDefault(arg => arg.NameColon?.Name.Identifier.Text == "interfaceType");
        if (namedArgument != null) {
            return namedArgument;
        }
        return argumentList.Arguments.Count > 0 ? argumentList.Arguments[0] : null;
    }

    /// <summary>
    /// 验证表达式是否表示接口类型（支持 typeof 、变量引用等）
    /// </summary>
    private (bool isInterface, INamedTypeSymbol typeSymbol) IsInterfaceTypeExpression(ExpressionSyntax expression, SemanticModel semanticModel) {

        INamedTypeSymbol typeSymbol;

        // 处理 typeof(IMyInterface) 形式
        if (expression is TypeOfExpressionSyntax typeOfExpr) {
            typeSymbol = semanticModel.GetTypeInfo(typeOfExpr.Type).Type as INamedTypeSymbol;
            return (IsInterfaceTypeSymbol(typeSymbol), typeSymbol);
        }

        // 处理变量/字段引用（如 var type = typeof(IMyInterface); 中的 type）
        var typeInfo = semanticModel.GetTypeInfo(expression);
        typeSymbol = typeInfo.Type as INamedTypeSymbol;
        return (IsInterfaceTypeSymbol(typeSymbol), typeSymbol);
    }

    /// <summary>
    /// 检查类型符号是否为接口
    /// </summary>
    private bool IsInterfaceTypeSymbol(INamedTypeSymbol typeSymbol) {
        // 排除空类型、值类型、委托（委托虽为引用类型但不是接口）
        if (typeSymbol == null
            || typeSymbol.IsValueType
            || typeSymbol.TypeKind == TypeKind.Delegate)
            return false;

        return typeSymbol.TypeKind == TypeKind.Interface;
    }
}
